package com.hdu.xxl_job_client.client;


import com.alibaba.fastjson2.JSONObject;
import com.hdu.xxl_job_client.config.XxlJobClientConfigProperties;
import com.hdu.xxl_job_client.enums.ExecutorBlockStrategyEnum;
import com.hdu.xxl_job_client.enums.MisfireStrategyEnum;
import com.hdu.xxl_job_client.req.job.XxlJobPageListReq;
import com.hdu.xxl_job_client.req.job.XxlJobSaveOrUpdateReq;
import com.hdu.xxl_job_client.res.job.XxlJobExecuteRes;
import com.hdu.xxl_job_client.res.job.XxlJobInfoRes;
import lombok.val;

import org.springframework.stereotype.Component;


import static com.hdu.xxl_job_client.utils.JsonUtils.json2obj;
import static com.hdu.xxl_job_client.utils.JsonUtils.obj2json;
import static com.hdu.xxl_job_client.utils.SimpleHttpClient.doPost;

import static java.lang.String.format;
import static java.util.Objects.isNull;
import static java.util.Optional.ofNullable;


@Component
public class XxlJobClient {

    private static final boolean UPDATE = Boolean.TRUE;
    private static final boolean SAVE = Boolean.FALSE;
    private static final boolean START = Boolean.FALSE;
    private static final boolean STOP = Boolean.TRUE;

    private final LoginClient loginClient;
    private final XxlJobClientConfigProperties clientConfigProperties;
    private final XxlJobGroupClient xxlJobGroupClient;

    public XxlJobClient(XxlJobClientConfigProperties clientConfigProperties, LoginClient loginClient, XxlJobGroupClient xxlJobGroupClient) {
        this.clientConfigProperties = clientConfigProperties;
        this.loginClient = loginClient;
        this.xxlJobGroupClient = xxlJobGroupClient;
    }

    public XxlJobExecuteRes saveXxlJob(XxlJobSaveOrUpdateReq xxlJobSaveOrUpdateReq) {

        val xxlJobRes = validate(xxlJobSaveOrUpdateReq, SAVE);
        if (xxlJobRes.isFail()) {
            return xxlJobRes;
        }

        val xxlJobGroupInfoRes = xxlJobGroupClient.getXxlJobGroup(xxlJobSaveOrUpdateReq.getJobGroupAppname());
        if (isNull(xxlJobGroupInfoRes)) {
            return XxlJobExecuteRes.fail(format("执行器不存在, appname : [%s]", xxlJobSaveOrUpdateReq.getJobGroupAppname()));
        }

        setDefaultParam(xxlJobSaveOrUpdateReq);

        val requestParams = obj2json(xxlJobSaveOrUpdateReq, true);
        requestParams.put("jobGroup", xxlJobGroupInfoRes.getId());
        val resJsonObj = doPost(
                clientConfigProperties.getJobInfoAddUrl(),
                requestParams,
                loginClient.getCOOKIE()
        );
        return json2obj(resJsonObj, XxlJobExecuteRes.class);
    }



    public XxlJobExecuteRes updateXxlJob(XxlJobSaveOrUpdateReq xxlJobSaveOrUpdateReq) {
        val xxlJobExecuteRes = validate(xxlJobSaveOrUpdateReq, UPDATE);
        if (xxlJobExecuteRes.isFail()) {
            return xxlJobExecuteRes;
        }

        // 获得执行器ID
        val xxlJobGroupInfoRes = xxlJobGroupClient.getXxlJobGroup(xxlJobSaveOrUpdateReq.getJobGroupAppname());
        if (isNull(xxlJobGroupInfoRes)) {
            return XxlJobExecuteRes.fail(format("执行器不存在, appname : [%s]", xxlJobSaveOrUpdateReq.getJobGroupAppname()));
        }

        setDefaultParam(xxlJobSaveOrUpdateReq);

        val requestParams = obj2json(xxlJobSaveOrUpdateReq, true);
        requestParams.put("id", xxlJobExecuteRes.getJobId());
        requestParams.put("jobGroup", xxlJobGroupInfoRes.getId());

        val resJsonObj = doPost(
                clientConfigProperties.getJobInfoUpdateUrl(),
                requestParams,
                loginClient.getCOOKIE()
        );

        return json2obj(resJsonObj, XxlJobExecuteRes.class);
    }

    public XxlJobExecuteRes deleteXxlJob(String jobGroupAppname, String jobDesc) {
        val xxlJobInfoRes = getXxlJob(jobGroupAppname, jobDesc);
        if (isNull(xxlJobInfoRes)) {
            return XxlJobExecuteRes.fail(format("任务 [%s] 不存在", jobDesc));
        }

        val requestParams = new JSONObject();
        requestParams.put("id", xxlJobInfoRes.getId());
        val resJsonObj = doPost(
                clientConfigProperties.getJobInfoDeleteUrl(),
                requestParams,
                loginClient.getCOOKIE()
        );

        return json2obj(resJsonObj, XxlJobExecuteRes.class);
    }

    private XxlJobInfoRes getXxlJob(String jobGroupAppname, String jobDesc) {
        XxlJobPageListReq xxlJobPageListReq = new XxlJobPageListReq();
        xxlJobPageListReq.setJobDesc(jobDesc);
        xxlJobPageListReq.setJobGroupAppName(jobGroupAppname);

        XxlJobExecuteRes xxlJobExecuteRes = getXxlJobPage(xxlJobPageListReq);
        if (xxlJobExecuteRes.isSuccess()) {
            val data = xxlJobExecuteRes.getData();
            return data.isEmpty() ? null : data.get(0);
        } else {
            return null;
        }
    }

    public XxlJobExecuteRes getXxlJobPage(XxlJobPageListReq jobInfoPageListReq) {
        val jobGroupAppName = jobInfoPageListReq.getJobGroupAppName();
        val jobDesc = jobInfoPageListReq.getJobDesc();
        if (jobGroupAppName == null || jobGroupAppName.isEmpty()) {
            return XxlJobExecuteRes.fail("执行器名称 不能为空");
        }
        if (jobDesc == null || jobDesc.isEmpty()) {
            return XxlJobExecuteRes.fail("任务描述 不能为空");
        }

        val xxlJobGroupInfoRes = xxlJobGroupClient.getXxlJobGroup(jobGroupAppName);
        if (isNull(xxlJobGroupInfoRes)) {
            return XxlJobExecuteRes.fail(format("执行器不存在, appname : [%s]", jobGroupAppName));
        }

        val requestParams = obj2json(jobInfoPageListReq, true);
        requestParams.put("jobGroup", xxlJobGroupInfoRes.getId());
        val resJsonObj = doPost(
                clientConfigProperties.getJobInfoPageListUrl(),
                requestParams,
                loginClient.getCOOKIE()
        );

        return json2obj(resJsonObj, XxlJobExecuteRes.class);
    }


    public XxlJobExecuteRes startXxlJob(String jobGroupAppname, String jobDesc) {
        return operateXxlJob(jobGroupAppname, jobDesc, START);
    }


    public XxlJobExecuteRes stopXxlJob(String jobGroupAppname, String jobDesc) {
        return operateXxlJob(jobGroupAppname, jobDesc, STOP);
    }

    private XxlJobExecuteRes operateXxlJob(String jobGroupAppname, String jobDesc, boolean isStop) {
        XxlJobInfoRes xxlJobInfoRes = getXxlJob(jobGroupAppname, jobDesc);
        if (isNull(xxlJobInfoRes)) {
            return XxlJobExecuteRes.fail(format("任务 [%s] 不存在", jobDesc));
        }
        val resJsonObj = doPost(
                isStop ? clientConfigProperties.getJobInfoStopJobUrl() : clientConfigProperties.getJobInfoStartJobUrl(),
                obj2json(xxlJobInfoRes, true),
                loginClient.getCOOKIE()
        );
        return json2obj(resJsonObj, XxlJobExecuteRes.class);
    }


    private void setDefaultParam(XxlJobSaveOrUpdateReq xxlJobSaveOrUpdateReq) {
        // 默认执行模式为BEAN
        xxlJobSaveOrUpdateReq.setGlueType("BEAN");
        switch (xxlJobSaveOrUpdateReq.getScheduleType()) {
            case NONE:
                xxlJobSaveOrUpdateReq.setScheduleConf("");
                break;
            case CRON:
                xxlJobSaveOrUpdateReq.setScheduleConf(xxlJobSaveOrUpdateReq.getScheduleConf());
                xxlJobSaveOrUpdateReq.setCronGenDisplay(xxlJobSaveOrUpdateReq.getScheduleConf());
                xxlJobSaveOrUpdateReq.setScheduleConfCron(xxlJobSaveOrUpdateReq.getScheduleConf());
                break;
            case FIX_RATE:
                xxlJobSaveOrUpdateReq.setScheduleConfFixRate(xxlJobSaveOrUpdateReq.getScheduleConf());
                break;
        }

        if (xxlJobSaveOrUpdateReq.getMisfireStrategy() == null) {
            // 默认调度过期策略 为忽略
            xxlJobSaveOrUpdateReq.setMisfireStrategy(MisfireStrategyEnum.DO_NOTHING);
        }
        if (xxlJobSaveOrUpdateReq.getExecutorBlockStrategy() == null) {
            // 默认阻塞策略为 串行执行
            xxlJobSaveOrUpdateReq.setExecutorBlockStrategy(ExecutorBlockStrategyEnum.SERIAL_EXECUTION);
        }
    }

    private XxlJobExecuteRes validate(XxlJobSaveOrUpdateReq xxlJobSaveOrUpdateReq, boolean isUpdate) {
        val jobGroupAppname = xxlJobSaveOrUpdateReq.getJobGroupAppname();
        val jobDesc = xxlJobSaveOrUpdateReq.getJobDesc();
        val author = xxlJobSaveOrUpdateReq.getAuthor();
        val executorRouteStrategy = xxlJobSaveOrUpdateReq.getExecutorRouteStrategy();
        val scheduleConf = xxlJobSaveOrUpdateReq.getScheduleConf();
        val executorHandler = xxlJobSaveOrUpdateReq.getExecutorHandler();

        if (jobGroupAppname == null || jobGroupAppname.isEmpty()) {
            return XxlJobExecuteRes.fail("执行器名称 不能为空");
        }
        if (jobDesc == null || jobDesc.isEmpty()) {
            return XxlJobExecuteRes.fail("任务描述 不能为空");
        }
        if (author == null || author.isEmpty()) {
            return XxlJobExecuteRes.fail("任务作者 不能为空");
        }
        if (executorRouteStrategy == null) {
            return XxlJobExecuteRes.fail("任务路由策略 不能为空");
        }
        if (scheduleConf == null || scheduleConf.isEmpty()) {
            return XxlJobExecuteRes.fail("任务调度配置 不能为空");
        }
        if (executorHandler == null || executorHandler.isEmpty()) {
            return XxlJobExecuteRes.fail("任务执行器 不能为空");
        }

        XxlJobInfoRes xxlJobInfoRes = getXxlJob(jobGroupAppname, jobDesc);
        if (isUpdate) {
            return ofNullable(xxlJobInfoRes)
                    .map(xJIR -> {
                        XxlJobExecuteRes xxlJobExecuteRes = XxlJobExecuteRes.success();
                        xxlJobExecuteRes.setJobId(xJIR.getId().toString());
                        return xxlJobExecuteRes;
                    })
                    .orElseGet(() -> XxlJobExecuteRes.fail(format("任务 [%s] 不存在", jobDesc)));
        } else {
            return ofNullable(xxlJobInfoRes)
                    .map(xJIR -> XxlJobExecuteRes.fail(format("任务 [%s] 已存在", jobDesc)))
                    .orElseGet(XxlJobExecuteRes::success);
        }
    }
}

